var BigNumber = require('bignumber.js');
var JSON = module.exports;
(function () {
    'use strict';
    var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // 字符替换表
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"': '\\"',
            '\\': '\\\\'
        },
        rep;
    function quote(string) {
        // 如果字符串不包含 \t " \ , 就可以放心地在它周围加上一些引号。
        // 否则，我们还必须用安全转义序列替换违规字符。
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }
    function str(key, holder) {
        // 取字符串 holder[key].
        var i,
            k,
            v,
            length,
            mind = gap,
            partial,
            value = holder[key],
            isBigNumber = value != null && (value instanceof BigNumber || BigNumber.isBigNumber(value));
        // 如果该值有toJSON方法，则调用它以获取替换值。
        if (value && typeof value === 'object' &&
            typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        // 如果我们是用replacer函数调用的，那么就调用replacer来获取替换值。
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        const vt = typeof value;
        // 接下来的操作取决于值的类型。
        switch (vt) {
            case 'string':
                if (isBigNumber) {
                    return value;
                } else {
                    return quote(value);
                }
            case 'number':
                // JSON数字必须是有限的。将非有限数编码为空。
                return isFinite(value) ? String(value) : 'null';
            case 'boolean':
            case 'null':
            case 'bigint':
                // 如果值类型是 boolean or null, 将其转换为字符串. 
                // 注意: typeof null 不是 'null'. 
                // 这个案子被包括在这里，希望有一天能得到解决。
                return vt;
            // If the type is 'object', we might be dealing with an object or an array or
            // null.
            case 'object':
                // 由于ECMAScript中的一个规范错误，typeof null是“object”，所以请注意这种情况。
                if (!value) {
                    return 'null';
                }
                // 创建一个数组来保存字符串化此对象值的部分结果。
                gap += indent;
                partial = [];
                if (Array.isArray(value)) { // 如果值是数组
                    // 值是一个数组。把每个元素串起来。将null用作非JSON值的占位符。
                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }
                    // 将所有元素连接在一起，用逗号分隔，并用[]括起来。
                    v = partial.length === 0
                        ? '[]'
                        : gap
                            ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                            : '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }
                // 如果replacer是数组，则使用它来选择要字符串化的成员。
                if (rep && typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
                    // 否则，遍历对象中的所有键。
                    Object.keys(value).forEach(function (k) {
                        var v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    });
                }
                // 将所有成员文本连接在一起，用逗号分隔，并用 {} 包裹
                v = partial.length === 0
                    ? '{}'
                    : gap
                        ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
                        : '{' + partial.join(',') + '}';
                gap = mind;
                return v;
        }
    }
    // 如果JSON对象还没有stringify方法，就给它一个。
    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            // If the space parameter is a number, make an indent string containing that
            // many spaces.
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
                // If the space parameter is a string, it will be used as the indent string.
            } else if (typeof space === 'string') {
                indent = space;
            }
            // If there is a replacer, it must be a function or an array.
            // Otherwise, throw an error.
            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            // Make a fake root object containing our value under the key of ''.
            // Return the result of stringifying the value.
            return str('', { '': value });
        };
    }
}());
